package org.nhindirect.stagent;

import java.util.ArrayList;
import java.util.Collection;

import javax.mail.internet.ContentType;
import javax.mail.internet.ParseException;

import org.nhindirect.stagent.EntityPart.EntityPartType;
import org.nhindirect.stagent.ProtocolException.ProtocolError;

public class MimeEntity 
{
    protected HeaderCollection m_headers;
    protected Body m_body;
    ContentType m_contentType;  // strongly typed, used internally for parsing

    public MimeEntity()
    {
    }
    
    public boolean hasHeaders()
    {
        return (this.m_headers != null && this.m_headers.size() > 0);
    }

    public HeaderCollection getHeaders()
    {
        if (this.m_headers == null)
        {
            this.m_headers = new HeaderCollection();
        }

        return m_headers;
    }
    
    public void setHeaders(HeaderCollection value)
    {
    	this.m_headers = value;
    }

    public Header getContentType()
    {
        return this.getHeaders().getHeader(Protocol.ContentTypeHeader);
    }
     
    public void setContentType(Header value)
    {
        this.getHeaders().setHeader(Protocol.ContentTypeHeader, value);
        this.m_contentType = null;
    }

    public Header getContentDisposition()
    {
    	return this.getHeaders().getHeader(Protocol.ContentDispositionHeader);
    }
    
    public void setContentDisposition(Header value)
    {
        this.getHeaders().setHeader(Protocol.ContentDispositionHeader, value);
    }

    public Header getContentTransferEncoding()
    {
        return this.getHeaders().getHeader(Protocol.ContentTransferEncodingHeader);
    }
        
    public void setContentTransferEncoding(Header value)
    {
        this.getHeaders().setHeader(Protocol.ContentTransferEncodingHeader, value);
    }

    public ContentType getParsedContentType()
    {

    	if (this.m_contentType == null)
        {
            Header contentTypeHeader = this.getContentType();
            if (contentTypeHeader != null)
            {
            	try
            	{
            		this.m_contentType = new ContentType(contentTypeHeader.getValue());
            	}
            	catch (ParseException e){}
            }
        }

        return this.m_contentType;
    }

    public boolean isMultiPart()
    {
        Header header = this.getContentType();
        if (header == null)
        {
            return false;
        }
        return Protocol.contains(header.getValue(), Protocol.MediaType_Multipart);
    }

    public Body getBody()
    {
        return this.m_body;
    }
    

	public void setBody(Body value)
	{
        this.m_body = value;
    }

    public boolean hasBody()
    {
        return (this.m_body != null);
    }
    
    public void applyBody(MimeEntity entity)
    {
        if (entity == null)
        {
            throw new IllegalArgumentException();
        }

        this.getHeaders().addUpdate(entity.getHeaders());
        this.setBody(entity.getBody());            
    }
    
    public void applyBody(MultipartEntity multipartEntity)
    {
        this.setParts(multipartEntity);
    }
    
    /// <summary>
    /// Skips/ignores Prologue and the Epilogue parts...
    /// </summary>
    /// <returns></returns>
    public Collection<MimeEntity> getParts()
    {
    	Collection<MimeEntity> retVal = new ArrayList<MimeEntity>();
    	
        if (!this.isMultiPart())
        {
            throw new IllegalStateException();
        }
        
        Collection<EntityPart> parts = this.getAllParts();
        
        for (EntityPart part : parts)
        {
            if (part.getType() == EntityPartType.SubPart)
            {
            	retVal.add(EntityParser.read(part.getSourceText()));
            }
        }
        
        return retVal;
    }
    
    public Collection<EntityPart> getAllParts()
    {
        return EntityParser.readBodyParts(this.m_body.getSourceText(), this.getParsedContentType().getParameter("boundary"));
    }
    
    public void setParts(MultipartEntity entities)
    {
        if (entities == null)
        {
            throw new IllegalArgumentException();
        }
        
        this.setParts(entities, entities.getContentType().toString());
    }
    
    public void setParts(Collection<MimeEntity> entities, String contentType)
    {
        if (entities == null)
        {
            throw new IllegalArgumentException();
        }
        
        if (contentType == null || contentType.length() == 0)
        {
            contentType = Protocol.MultiPartType_Mixed;
        }
        this.setContentType(new Header(Protocol.ContentTypeHeader, contentType));
        this.setBody(new Body(EntitySerializer.Default.serialize(entities, this.getParsedContentType().getParameter("boundary"))));
    }
            
    public void verifyContentType(String expectedType)
    {
    	try
    	{
    		this.verifyContentType(new ContentType(expectedType));
    	}
    	catch (ParseException ex){};
    }

    public void verifyContentType(ContentType expectedType)
    {
        ContentType parsedType = this.getParsedContentType();
        if (parsedType == null || !Protocol.equals(parsedType.toString(), expectedType.toString()))
        {
            throw new ProtocolException(ProtocolError.ContentTypeMismatch);
        }
    }
    
    public void verifyMediaType(String expectedType)
    {
        ContentType parsedType = this.getParsedContentType();
        if (parsedType == null || !Protocol.equals(parsedType.getBaseType(), expectedType))
        {
            throw new ProtocolException(ProtocolError.ContentTypeMismatch);
        }
    }

    public void verifyContentTypeParameter(String key, String expectedValue)
    {
        ContentType parsedType = this.getParsedContentType();
        String value = parsedType.getParameter(key);
        if (parsedType == null || !Protocol.equals(value, expectedValue))
        {
            throw new ProtocolException(ProtocolError.ContentTypeMismatch);
        }
    }
    
    public void verifyTransferEncoding(String expectedEncoding)
    {
        Header transferEncodingHeader = this.getContentTransferEncoding();
        if (transferEncodingHeader == null || transferEncodingHeader.getValue().compareToIgnoreCase((expectedEncoding)) != 0)
        {
            throw new ProtocolException(ProtocolError.TransferEncodingMismatch);
        }
    }

    @Override
    public String toString()
    {
        return EntitySerializer.Default.serialize(this);
    }
    
    public MimeEntityCollection toParts()
    {
        if (!this.isMultiPart())
        {
            throw new IllegalStateException();
        }
        
        return new MimeEntityCollection(this.getContentType().getValue(), this.getParts());
    }
}
